/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gestor.base.de.datos;

import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Ana Silvia Gonzales
 * @author Diego Enriquez
 */
public class Check implements Serializable{
    
    Stack<String>[] condiciones;
    
    public Check(Stack<String>[] condiciones)
    {
        
        this.condiciones=new Stack[2];
        this.condiciones[0]=(Stack<String>) condiciones[0].clone();
        this.condiciones[1]=(Stack<String>) condiciones[1].clone();
        
        
    }
    
    /**
     * Revisa si el registro cumple con las condiciones del check
     * @param tabla
     * @param registro
     * @return true si cumple, de lo contrario
     */
    public boolean checks(Table tabla, ArrayList<String> registro)
    {
        
        boolean resp;
        
        resp=evalWhere(this.condiciones,tabla,registro);
        
        return resp;
        
    }
    
    /**
     * Obtiene el valor del campo en la posicion indicada
     * @param tabla
     * @param registros
     * @param pos
     * @return array con valor y tipo del campo
     */
    private String[] getValCampo(Table tabla,ArrayList<String> registros, int pos)
    {
        
        String[] resp=new String[2];
        resp[0]=registros.get(pos);
        resp[1]=tabla.getTipoCol(pos);
        
        
        return resp;
        
    }
    
    /**
     * Convierte los tipos diferentes que se comparan
     * @param tipo1
     * @param tipo2
     * @return array con los tipos convertidso
     */
    private String[] convTipo(String tipo1, String tipo2)
    {
        
        String[] resp=new String[2];
        
        if(tipo1.equals("CHAR")||(tipo2.equals("CHAR")))
        {
            resp[0]="CHAR";
            resp[1]="CHAR";
        }
        else if(tipo1.equals("FLOAT")||tipo2.equals("FLOAT"))
        {
            if(tipo1.equals("INT")||tipo2.equals("INT"))
            {
                resp[0]="FLOAT";
                resp[1]="FLOAT";
            }
            else
            {
                resp=null;
            }
        }
        else
        {
            resp=null;
        }
        
        return resp;
        
    }
    
    /**
     * Evalua un registro con la condicion indicada
     * @param condicion1
     * @param tabla
     * @param registro
     * @return 
     */
    private boolean evalWhere(Stack<String>[] condicion1, Table tabla, ArrayList<String> registro) {
        boolean resp;
        boolean error = false;
        Stack<String>[] condicion = new Stack[2];
        condicion[0]=(Stack<String>) condicion1[0].clone();
        condicion[1]=(Stack<String>) condicion1[1].clone();
        String valTipo;
        Stack<String>[] valores=new Stack[2];
        valores[0] = new Stack<>();
        valores[1] = new Stack<>();

        while ((condicion[0].size() > 0)) {
            valTipo=condicion[1].pop();
            if ((valTipo.equals("OTRO"))||(valTipo.equals("BOOLEAN"))) {
                condicion[1].push(valTipo);
 
                String op1;
                String op2;
                String opera;
                String tipo1;
                String tipo2;

                opera = condicion[0].pop();
                condicion[1].pop();
                op1 = valores[0].pop().toUpperCase();
                tipo1 = valores[1].pop();
                if(opera.equals("NOT"))
                {
                    op2="NOT";
                    tipo2="NOT";
                }
                else
                {
                    op2 = valores[0].pop().toUpperCase();
                    tipo2 = valores[1].pop();
                }
                switch (tipo1) {
                    case "COLUMNA":
                        String[] nuevos = getValCampo(tabla, registro, tabla.getColNum(op1));
                        if (nuevos != null) {
                            op1 = nuevos[0];
                            tipo1 = nuevos[1];
                        } else {
                            error = true;
                        }
                        break;
                    case "DATE":
                        op1=op1.substring(1, op1.length() - 1);
                        break;                    
                }
                switch (tipo2) {
                    case "COLUMNA":
                        String[] nuevos = getValCampo(tabla, registro, tabla.getColNum(op2));
                        if (nuevos != null) {
                            op2 = nuevos[0];
                            tipo2 = nuevos[1];
                        } else {
                            error = true;
                        }
                        break;
                    case "DATE":
                        op2=op2.substring(1, op2.length() - 1);
                        break;
                }

                if (!error) {
                    
                    if((!tipo1.equals(tipo2))&&(!opera.equals("NOT")))
                    {
                        String[] nuevos=convTipo(tipo1,tipo2);
                        if(nuevos==null)
                        {
                            error=true;
                        }
                        else
                        {
                            tipo1=nuevos[0];
                            tipo2=nuevos[1];
                        }
                    }
                }
                
                if((!error)&&((op2.equals("NULL")||(op1.equals("NULL")))||(op2.equals("'NULL'")||(op1.equals("'NULL'")))))
                {
                    if(op1.equals("NULL"))
                    {
                        op1="'NULL'";
                    }
                    if(op2.equals("NULL"))
                    {
                        op2="'NULL'";
                    }
                    if(comparaNull(op2,op1,opera))
                    {
                        valores[0].push("T");
                        valores[1].push("RES");
                    }
                    else
                    {
                        valores[0].push("F");
                        valores[1].push("RES");
                    }
                }
                
                else if(!error)
                {
                    valores[1].push(tipo2);
                    valores[1].push(tipo1);

                    switch (valores[1].peek()) {

                        case "INT":
                        case "FLOAT": {
                            valores[1].pop();
                            valores[1].pop();
                            float val1 = Float.parseFloat(op2);
                            float val2 = Float.parseFloat(op1);
                            if (compara(val1,val2,opera)) {
                                valores[0].push("T");
                                valores[1].push("RES");
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        }
                        case "CHAR":
                            valores[1].pop();
                            valores[1].pop();
                            if (compara(op2,op1,opera)) {
                                valores[0].push("T");
                                valores[1].push("RES");
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        case "RES":
                        {
                            valores[1].pop();
                            valores[1].pop();
                            boolean val1,val2;
                            
                            if(op1.equals("T"))
                            {
                                val2=true;
                            }
                            else
                            {
                                val2=false;
                            }
                            
                            if(op2.equals("T"))
                            {
                                val1=true;
                            }
                            else
                            {
                                val1=false;
                            }
                            
                            if (compara(val1,val2,opera)) {
                                valores[0].push("T");
                                valores[1].push("RES");
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        }
                        case "DATE": {
                            Date val1 = null;
                            Date val2 = null;
                            valores[1].pop();
                            valores[1].pop();
                            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                            try {
                                val1 = df.parse(op2);
                                val2 = df.parse(op1);
                            } catch (ParseException ex) {
                                Logger.getLogger(GestorBaseDeDatos.class.getName()).log(Level.SEVERE, null, ex);
                            }
                            if ((val1 != null) && (val2 != null)) {
                                if (compara(val1,val2,opera)) {
                                    valores[0].push("T");
                                    valores[1].push("RES");
                            }   else {
                                    valores[0].push("F");
                                    valores[1].push("RES");
                                }
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        }
                    }
                }


            }
            else
            {
                valores[0].push(condicion[0].pop());
                valores[1].push(valTipo);
            }
        }

        if(!error)
        {
            if (valores[0].size() < 1) {
                //AGREGAR ERROR!!!
                resp = false;
            } else {
                if(valores[1].peek().equals("RES"))
                {
                    if(valores[0].peek().equals("T"))
                    {
                        resp=true;
                    }
                    else
                    {
                        resp=false;
                    }
                }
                else
                {
                    //AGREGAR ERROR!!!
                    resp=false;
                }
            }
        }
        else
        {
            resp=false;
        }
            
        
        return resp;

    }
    
    /**
     * Compara dos valores null
     * @param val1
     * @param val2
     * @param opera
     * @return resultado de la comparacion
     */
    private boolean comparaNull(String val1,String val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "=":
            {
                if(val1.equals(val2))
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
               
            case "<>":
            {
                if(val1.equals(val2))
                {
                    resp=false;
                }
                else
                {
                    resp=true;
                }
                break;
            }
                
            default:
            {
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores float
     * @param val1
     * @param val2
     * @param opera
     * @return resultado de la comparacion
     */
    private boolean compara(float val1,float val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "<":
            {
                if(val1<val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">":
            {
                if(val1>val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            } 
                
            case "<=":
            {
                if(val1<=val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">=":
            {
                if(val1>=val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "=":
            {
                if(val1==val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "<>":
            {
                if(val1!=val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            default:
            {
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores string
     * @param val1
     * @param val2
     * @param opera
     * @return resultado de la comparacion
     */
    private boolean compara(String val1,String val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "<":
            {
                if(val1.compareTo(val2)<0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">":
            {
                if(val1.compareTo(val2)>0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            } 
                
            case "<=":
            {
                if(val1.compareTo(val2)<=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">=":
            {
                if(val1.compareTo(val2)>=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "=":
            {
                if(val1.compareTo(val2)==0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "<>":
            {
                if(val1.compareTo(val2)!=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            default:
            {
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores tipo Date
     * @param val1
     * @param val2
     * @param opera
     * @return resultado de la comparación
     */
    private boolean compara(Date val1,Date val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "<":
            {
                if(val1.compareTo(val2)<0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">":
            {
                if(val1.compareTo(val2)>0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            } 
                
            case "<=":
            {
                if(val1.compareTo(val2)<=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">=":
            {
                if(val1.compareTo(val2)>=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "=":
            {
                if(val1.compareTo(val2)==0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "<>":
            {
                if(val1.compareTo(val2)!=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            default:
            {
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores tipo boolean
     * @param val1
     * @param val2
     * @param opera
     * @return resultado de la comparacion
     */
    private boolean compara(boolean val1,boolean val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "AND":
            {
                if(val1 && val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "OR":
            {
                if(val1 || val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "NOT":
            {
                if(val1)
                {
                    resp=false;
                }
                else
                {
                    resp=true;
                }
                break;
            }
                
            default:
            {
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
}
